package com.radiant.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.UUID;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.UriBuilder;

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimplePBEConfig;
import org.jasypt.salt.RandomSaltGenerator;
import org.jasypt.salt.SaltGenerator;
import org.jasypt.util.password.BasicPasswordEncryptor;

import com.radiant.data.entity.User;
import com.radiant.data.entity.UserDAO;
import com.radiant.exceptions.RadiantAuthenticationException;

@Path("/authentication")
public class UserAuthenticationServiceImpl implements UserAuthenticationService {

	@Inject 
	private UserDAO userDAO;
	
	private static String SEPARATOR = "|";

	private static long TTL = 60*60*1000L;

	private StandardPBEStringEncryptor jasypt = new StandardPBEStringEncryptor();

	private BasicPasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor();

	public UserAuthenticationServiceImpl() {
		super();
		SimplePBEConfig simplePBEConfig = new SimplePBEConfig();
		simplePBEConfig.setAlgorithm("PBEWithMD5AndTripleDES");
		simplePBEConfig.setKeyObtentionIterations(10000);

		SaltGenerator saltGenerator = new RandomSaltGenerator();
		saltGenerator.generateSalt(16);

		simplePBEConfig.setSaltGenerator(saltGenerator);
		simplePBEConfig.setPassword("!gthyeasa76%");
		
		jasypt.setConfig(simplePBEConfig);
		jasypt.initialize();
	}
	
	public List<String> getProtectedPaths() {
		List<String> paths = new ArrayList<String>();
		return paths;
	}

	public String generateEncryptedPassword(String password) {
		return passwordEncryptor.encryptPassword(password);
	}

	@POST @Path("/authenticateUser")
	@Consumes("application/x-www-form-urlencoded") 
	@Produces("application/json")
	public String authenticateUser(@FormParam("userName") String userName, 
			@FormParam("password") String password) {
		User user = userDAO.findByUserName(userName);
		if (user != null) {
			String encryptedPassword = user.getPassword();
			if (passwordEncryptor.checkPassword(password, encryptedPassword)) {
				// this is the authentication token user will send in order to use the web service
				return jasypt.encrypt(generateNewToken(userName));	
			} else {
				throw new RadiantAuthenticationException("Invalid credentials");
			}
		} else {
			throw new RadiantAuthenticationException("Invalid credentials");
		}
	}

	private String generateNewToken(String userName) {
		StringBuilder key = new StringBuilder(UUID.randomUUID().toString().toUpperCase());
		key.append(SEPARATOR).append(userName).append(SEPARATOR).append(new Date().getTime());
		return key.toString();
	}

	@POST @Path("/renewToken")
	@Consumes("application/x-www-form-urlencoded") 
	@Produces("application/json")
	public String extendToken(@FormParam("token") String token) {
		UserToken userToken = extractUserToken(token);
		if ((new Date().getTime() - userToken.creationDateTime) < TTL) {
			return generateNewToken(userToken.userName);
		}
		throw new RadiantAuthenticationException("Invalid credentials");
	}

	@POST @Path("/validateToken")
	@Consumes("application/x-www-form-urlencoded") 
	@Produces("application/json")
	public boolean validateToken(@FormParam("token") String token) {
		if (token != null & extractUserToken(token) != null) {
			return true;
		} else {
			return false;
		}
	}

	private UserToken extractUserToken(String token) {
		String decryptedToken = jasypt.decrypt(token);
		StringTokenizer st = new StringTokenizer(decryptedToken, SEPARATOR);
		UserToken userToken = null;
		while (st.hasMoreTokens()) {
			String randomId = st.nextToken();
			String userName = st.nextToken();
			long creationDateTime = Long.valueOf(st.nextToken());
			userToken = new UserToken(randomId, userName, creationDateTime);
		}
		if ((new Date().getTime() - userToken.creationDateTime) < TTL) {
			return userToken;
		}
		throw new RadiantAuthenticationException("Invalid credentials");
	}

	public User getUser(@FormParam("token") String token) {
		UserToken userToken = extractUserToken(token);
		return userDAO.findByUserName(userToken.userName);
	}

	class UserToken {
		String randomId;
		String userName;
		long creationDateTime; 
		
		UserToken(String randomId, String userName, long creationDateTime) {
			this.randomId = randomId;
			this.userName = userName;
			this.creationDateTime = creationDateTime;
		}
	}
	
}
